package org.spruice;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.picocontainer.DefaultPicoContainer;
import org.spruice.proxy.BeanCallback;
import org.spruice.proxy.ProxyCreator;

import java.lang.reflect.Method;
import java.util.Properties;

public class BeanConfig<T> implements NamedBean, ContextEntry {

    private final BeanDefinitionBuilder builder;
    private ProxyCreator objFactory;
    private Class<?> proxyClzz;

    public BeanConfig(BeanDefinitionBuilder builder, ProxyCreator fact, Class<T> clzz) {
        this.builder = builder;
        this.objFactory = fact;
        this.proxyClzz = clzz;
    }

    public BeanConfig<T> destroyWith(String destroyMethod) {
        builder.setDestroyMethodName(destroyMethod);
        return this;
    }

    public BeanConfig<T> initWith(String initMethod) {
        builder.setInitMethodName(initMethod);
        return this;
    }

    public String getName() {
        return builder.getName();
    }

    public BeanDefinitionBuilder getBuilder() {
        return builder;
    }

    public BeanConfig<T> startWith(String startMethod) {
        builder.setStartMethod(startMethod);
        return this;
    }

    public T initWith() {
        MethodInterceptor advice = new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                initWith(method.getName());
                return null;
            }
        };
        return createProxy(advice);
    }

    public T destroyWith() {
        MethodInterceptor advice = new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                destroyWith(method.getName());
                return null;
            }
        };
        return createProxy(advice);
    }

    public T startWith() {
        MethodInterceptor advice = new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                startWith(method.getName());
                return null;
            }
        };
        return createProxy(advice);
    }

    T createProxy(final MethodInterceptor advice) {
        return (T) objFactory.imposterise(new BeanCallback(getName(), advice), proxyClzz);
    }


    public BeanConfig<T> set(Method propertyName, Object[] value) {
        builder.addPropertyValue(propertyName, value);
        return this;
    }

    public T set() {
        MethodInterceptor advice = new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                set(method, args);
                return null;
            }
        };
        return createProxy(advice);
    }

    public static String parseProperty(String name) {
        String property = name.substring(3);
        return CamelCaseNamer.format(property);
    }

    public T stopWith() {
        MethodInterceptor advice = new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                builder.setStopMethod(method.getName());
                return null;
            }
        };
        return createProxy(advice);
    }

    public <N> BeanConfig<N> configureWith(Class<N> targetInterface) {
        proxyClzz = targetInterface;
        return (BeanConfig<N>) this;
    }

    public void addTo(DefaultPicoContainer context, ModuleLifecycle life) {
        builder.addTo(context, life);
/*
        AbstractBeanDefinition def = builder.getBeanDefinition();
        def.setPrimary(isPrimary);

        String beanName = getName();
        context.registerBeanDefinition(beanName, def);
        if (getStartMethod() != null || getStopMethod() != null) {
            BeanDefinitionBuilder startBuilder = BeanDefinitionBuilder.genericBeanDefinition(ContextLifecycle.class);
            //force depends on so spring maintains proper start/stop order
            startBuilder.addDependsOn(beanName);
            startBuilder.addPropertyReference("target", getName());
            if (getStartMethod() != null)
                startBuilder.addPropertyValue("startMethod", getStartMethod());
            if (getStopMethod() != null)
                startBuilder.addPropertyValue("stopMethod", getStopMethod());
            context.registerBeanDefinition(beanName + "LifecycleMethods", startBuilder.getBeanDefinition());
        }
*/
    }

    public BeanConfig<T> configure(Properties properties) {
        this.builder.addProperties(properties);
        return this;
    }
}
